using System;
using System.Collections.Generic;
#if SANFORDMIDI
using Sanford.Multimedia.Midi;
#endif

namespace Kioskvalt.MidiSequencer
{

	public class MidiWrapper
	{
		static Dictionary<int, MidiDeviceWrapper> devices;

		public static void Start()
		{
			devices = new Dictionary<int, MidiDeviceWrapper>();
			LoadAllDevices();
		}

		static void LoadAllDevices()
		{
			List<int> ids = GetDeviceIDs();
			for (int j = 0; j < ids.Count; j++)
			{
				Console.WriteLine("Device #" + j + ": " + GetDeviceName(ids[j]));

				if (!devices.ContainsKey(j))
					devices.Add(j, new MidiDeviceWrapper(j));
				else
					devices[j] = new MidiDeviceWrapper(j);
			}
		}

		public static void ReloadDevices()
		{
			CloseAll();
		}

		public static void Stop()
		{
			CloseAll();
		}

		static void CloseAll()
		{
			Dictionary<int, MidiDeviceWrapper>.Enumerator enu = devices.GetEnumerator();
			while (enu.MoveNext())
			{
				if (enu.Current.Value != null)
					enu.Current.Value.Close();
			}
			// devices.Clear();
		}

		public static List<int> GetDeviceIDs()
		{
			List<int> list = new List<int>();
#if SANFORDMIDI
			int n = OutputDevice.DeviceCount;
			for (int j = 0; j < n; j++)
				list.Add(j);
#endif 
			return list;
		}

		public static string GetDeviceName(int id)
		{
#if SANFORDMIDI
			if (id >= 0 && id < OutputDevice.DeviceCount)
			{
				string nam = "Unnamed";
				try
				{
					MidiOutCaps caps = OutputDevice.GetDeviceCapabilities(id);
					nam = caps.name;
				}
				catch (Exception)
				{
				}
				return id.ToString("000") + ": " + nam;
			}
#endif
			return "";
		}

		//	static void ensureDevice(int id)
		//	{
		//		if (!devices.ContainsKey(id))
		//			if (id >= 0 && id < OutputDevice.DeviceCount)
		//				devices.Add(id, new MidiDeviceWrapper(id));
		//	}

		public static void QueueNoteOn(int device, int channel, int note, int velocity)
		{
#if SANFORDMIDI
			// Console.WriteLine("Midi: NOTE-ON: device=" + device + ", channel=" + channel + ", note=" + note + ", velocity=" + velocity);
			//	ensureDevice(device);
			if (devices.ContainsKey(device))
				//	if (devices[device].device != null)
				//		devices[device].device.Send(new ChannelMessage(ChannelCommand.NoteOn, channel, note, velocity));
				devices[device].queue.Enqueue(new ChannelMessage(ChannelCommand.NoteOn, channel, note, velocity));
#endif
			}

		public static void QueueNoteOff(int device, int channel, int note)
		{
#if SANFORDMIDI
			// Console.WriteLine("Midi NOTE-OFF: device=" + device + ", channel=" + channel + ", note=" + note);
			//	ensureDevice(device);
			if (devices.ContainsKey(device))
				//		if (devices[device].device != null)
				//		devices[device].device.Send(new ChannelMessage(ChannelCommand.NoteOff, channel, note));
				devices[device].queue.Enqueue(new ChannelMessage(ChannelCommand.NoteOff, channel, note));
#endif
		}

		public static void QueueAllNoteOff(int device, int channel)
		{
#if SANFORDMIDI
			// Console.WriteLine("Midi NOTE-OFF: device=" + device + ", channel=" + channel + ", note=" + note);
			//ensureDevice(device);
			if (devices.ContainsKey(device))
				//	if (devices[device].device != null)
			{
			//	ChannelStopper st = new ChannelStopper();
			//	ChannelMessage cm = new ChannelMessage(ChannelCommand.NoteOff, channel, 0)
			//	st.Process(  cm);
			//	devices[device].device.Send(cm);
			}
#endif
		}

		public static void QueueControlChange(int device, int channel, int control, int value)
		{
#if SANFORDMIDI
			// Console.WriteLine("Midi CONTROL-CHANGE: device=" + device + ", channel=" + channel + ", control=" + control+", value="+value);
			//	ensureDevice(device);
			if (devices.ContainsKey(device))
				//		if (devices[device].device != null)
				//		devices[device].device.Send(new ChannelMessage(ChannelCommand.Controller, channel, control, value));
				devices[device].queue.Enqueue(new ChannelMessage(ChannelCommand.Controller, channel, control, value));
#endif
		}

		public static void QueueProgramChange(int device, int channel, int program)
		{
#if SANFORDMIDI
			//	ensureDevice(device);
			if (devices.ContainsKey(device))
				//		if (devices[device].device != null)
				//		devices[device].device.Send(new ChannelMessage(ChannelCommand.ProgramChange, channel, program));
				devices[device].queue.Enqueue(new ChannelMessage(ChannelCommand.ProgramChange, channel, program));
#endif
		}

		public static void ClearQueue()
		{
#if SANFORDMIDI
			/*
			Dictionary<int, MidiDeviceWrapper>.Enumerator enu = devices.GetEnumerator();
			while (enu.MoveNext())
			{
				enu.Current.Value.queue.Clear();

			}*/
#endif
		}

		public static void ProcessQueue()
		{
#if SANFORDMIDI
			// kolla om vi behöver öppna några nya devices
			Dictionary<int, MidiDeviceWrapper>.Enumerator enu = devices.GetEnumerator();
			while (enu.MoveNext())
			{
				MidiDeviceWrapper dev = enu.Current.Value;

				if (!dev.IsOpen)
					dev.Open();

				while (dev.queue.Count > 0)
				{
					IMidiMessage msg = dev.queue.Dequeue();
					if (msg is ChannelMessage)
						dev.device.Send(msg as ChannelMessage);
					//	else if (msg is ChannelMessage)
					//		dev.device.Send(msg as ChannelMessage);
					//	else if (msg is ChannelMessage)
					//		dev.device.Send(msg as ChannelMessage);
					//	else if (msg is ChannelMessage)
					//		dev.device.Send(msg as ChannelMessage);
				}
			}
#endif
		}


		public static string GetControllerName(int id)
		{
			Dictionary<int, string> d = new Dictionary<int, string>();

			// från http://www.indiana.edu/~emusic/cntrlnumb.html

			d.Add(0x00, "Bank Select");
			d.Add(0x01, "Modulation Wheel");
			d.Add(0x02, "Breath Contoller");

			d.Add(0x04, "Foot Controller");
			d.Add(0x05, "Portamento Time");
			d.Add(0x06, "Data Entry MSB");
			d.Add(0x07, "Main Volume");
			d.Add(0x08, "Balance");

			d.Add(0x0A, "Pan");

			d.Add(0x0C, "Effect Control 1");
			d.Add(0x0D, "Effect Control 2");

			d.Add(0x10, "General Purpose Controller 1");
			d.Add(0x11, "General Purpose Controller 1");
			d.Add(0x12, "General Purpose Controller 1");
			d.Add(0x13, "General Purpose Controller 1");

			d.Add(0x40, "Damper Pedal (Sustain) [Data Byte of 0-63=0ff, 64-127=On]");
			d.Add(0x41, "Portamento");
			d.Add(0x42, "Sostenuto");
			d.Add(0x43, "Soft Pedal");
			d.Add(0x44, "Legato Footswitch");
			d.Add(0x45, "Hold 2");
			d.Add(0x46, "Sound Variation");
			d.Add(0x47, "Timbre/Harmonic Content");
			d.Add(0x48, "Release Time");
			d.Add(0x49, "Attack Time");
			d.Add(0x4A, "Brightness");

			d.Add(0x54, "Portamento Control");


			d.Add(0x5B, "Effects 1 Depth (previously External Effects Depth)");
			d.Add(0x5C, "Effects 2 Depth (previously Tremolo Depth)");
			d.Add(0x5D, "Effects 3 Depth (previously Chorus Depth)");
			d.Add(0x5E, "Effects 4 Depth (previously Detune Depth)");
			d.Add(0x5F, "Effects 5 Depth (previously Phaser Depth)");
			d.Add(0x60, "Data Increment");
			d.Add(0x61, "Data Decrement");

			d.Add(0x62, "Non-Registered Parameter Number LSB");
			d.Add(0x63, "Non-Registered Parameter Number LSB");
			d.Add(0x64, "Registered Parameter Number LSB");
			d.Add(0x65, "Registered Parameter Number MSB");





			d.Add(0x79, "Reset All Controllers");
			d.Add(0x7A, "Local Control");
			d.Add(0x7B, "All Notes Off");
			d.Add(0x7C, "Omni Off");
			d.Add(0x7D, "Omni On");
			d.Add(0x7E, "Mono On (Poly Off)");
			d.Add(0x7F, "Poly On (Mono Off)");

			if (d.ContainsKey(id))
				return d[id];

			return "";
		}
	}
}
